#include "view/MainFrame.h"
#include "components/TimeSequencePanel.h"
#include "utils/CSV.h"
#include "wx/gtk/menuitem.h"

#include <cstdlib>
#include <queue>

using components::TimeSequencePanel;
using models::Process;
using models::TimePoint;

inline std::vector<TimePoint> simulate(const std::vector<Process> &processes) {
  std::priority_queue<Process> ready_queue;
  for (auto p : processes) {
    ready_queue.push(p);
  }
  int t = 0;
  std::priority_queue<Process> io_queue;
  std::vector<TimePoint> time_points;
  Process process_in_cpu;
  bool cpu_busy = false;
  int process_in_cpu_time = 0;

  while (!ready_queue.empty() || !io_queue.empty() || cpu_busy) {
    if (cpu_busy && process_in_cpu.status == 0 &&
        process_in_cpu.c1 == process_in_cpu_time) {
      process_in_cpu.status = 1;
      io_queue.push(process_in_cpu);
      time_points.push_back({t, process_in_cpu.pid, "c1", "finished"});
      time_points.push_back({t, process_in_cpu.pid, "io", "started"});
      cpu_busy = false;
      process_in_cpu_time = 0;
      continue;
    } else if (cpu_busy && process_in_cpu.status == 2 &&
               process_in_cpu.c2 == process_in_cpu_time) {
      time_points.push_back({t, process_in_cpu.pid, "c2", "finished"});
      cpu_busy = false;
      process_in_cpu_time = 0;
      continue;
    }
    if (!ready_queue.empty() && !cpu_busy) {
      auto p = ready_queue.top();
      ready_queue.pop();
      if (p.status == 0 || p.status == 2) {
        process_in_cpu = p;
        cpu_busy = true;
        if (p.status == 0) {
          time_points.push_back({t, p.pid, "c1", "started"});
        } else {
          time_points.push_back({t, p.pid, "c2", "started"});
        }
      } else {
        io_queue.push(p);
      }
      continue;
    }
    if (!io_queue.empty()) {
      auto p = io_queue.top();
      io_queue.pop();

      if (p.io == 0) {
        p.status = 2;
        ready_queue.push(p);
        time_points.push_back({t, p.pid, "io", "finished"});
        continue;
      } else {
        p.io--;
        io_queue.push(p);
      }
    }
    if (cpu_busy) {
      process_in_cpu_time++;
    }
    t++;
  }
  return time_points;
}

namespace view {
MainFrame::MainFrame(const wxString &title, const wxPoint &pos,
                     const wxSize &size)
    : wxFrame(NULL, wxID_ANY, title, pos, size) {
  wxMenu *menuFile = new wxMenu;
  wxMenuItem *openItem = new wxMenuItem(menuFile, wxID_OPEN, "&Open\tCtrl+O");
  menuFile->Append(openItem);
  menuFile->Append(wxID_EXIT);

  wxMenu *menuHelp = new wxMenu;
  menuHelp->Append(wxID_ABOUT);

  wxMenu *menuView = new wxMenu;
  wxMenuItem *threeStateTimeSequence =
      new wxMenuItem(menuView, wxID_ANY, "&Three State Time Sequence");
  wxMenuItem *fiveStateTimeSequence =
      new wxMenuItem(menuView, wxID_ANY, "&Five State Time Sequence");
  wxMenuItem *sevenStateTimeSequence =
      new wxMenuItem(menuView, wxID_ANY, "&Seven State Time Sequence");
  menuView->Append(threeStateTimeSequence);
  menuView->Append(fiveStateTimeSequence);
  menuView->Append(sevenStateTimeSequence);

  wxMenuBar *menuBar = new wxMenuBar;
  menuBar->Append(menuFile, "&File");
  menuBar->Append(menuView, "&View");
  menuBar->Append(menuHelp, "&Help");

  SetMenuBar(menuBar);

  CreateStatusBar();
  SetStatusText("Welcome to wxWidgets!");

  Bind(wxEVT_MENU, &MainFrame::OnExit, this, wxID_EXIT);
  Bind(wxEVT_MENU, &MainFrame::OnAbout, this, wxID_ABOUT);
  Bind(wxEVT_MENU, &MainFrame::OnOpen, this, wxID_OPEN);
  Bind(wxEVT_MENU, &MainFrame::OnThreeStateTimeSequenceMenuItemClicked, this,
       threeStateTimeSequence->GetId());
  Bind(wxEVT_MENU, &MainFrame::OnFiveStateTimeSequenceMenuItemClicked, this,
       fiveStateTimeSequence->GetId());
  Bind(wxEVT_MENU, &MainFrame::OnSevenStateTimeSequenceMenuItemClicked, this,
       sevenStateTimeSequence->GetId());
}

MainFrame::~MainFrame() {}

void MainFrame::OnExit(wxCommandEvent &event) { Close(true); }

void MainFrame::OnAbout(wxCommandEvent &event) {
  wxMessageBox("This is a wxWidgets' Hello World sample", "About Hello World",
               wxOK | wxICON_INFORMATION);
}

void MainFrame::OnOpen(wxCommandEvent &event) {
  wxFileDialog openFileDialog(this, _("Open CSV file"), "", "",
                              "TXT files (*.csv)|*.csv",
                              wxFD_OPEN | wxFD_FILE_MUST_EXIST);

  if (openFileDialog.ShowModal() == wxID_CANCEL) {
    return;
  }

  wxString path = openFileDialog.GetPath();
  utils::CSV csv(path.ToStdString());
  std::vector<Process> processes;
  try {
    auto data = csv.read();
    int priority = 0;
    for (const auto &row : data) {
      processes.push_back({row[0], priority++, std::stoi(row[1]),
                           std::stoi(row[2]), std::stoi(row[3]), 0});
    }

    auto time_points = simulate(processes);
    TimeSequencePanel *panel =
        new TimeSequencePanel(this, processes, time_points);
    SetSizer(new wxBoxSizer(wxVERTICAL));
    GetSizer()->Add(panel, 1, wxEXPAND | wxALL, 5);
    Layout();
  } catch (const std::exception &e) {
    wxMessageBox(e.what(), "Error", wxOK | wxICON_ERROR);
    std::cerr << e.what() << std::endl;
  }
}

void MainFrame::OnThreeStateTimeSequenceMenuItemClicked(wxCommandEvent &event) {
  wxDialog *dialog = new wxDialog(this, wxID_ANY, "Three State Time Sequence",
                                  wxDefaultPosition, wxSize(1800, 600));
  system("dot -Tpng scripts/3.dot -o 3ts.png");
  wxImage image;
  if (!image.LoadFile("3ts.png", wxBITMAP_TYPE_PNG)) {
    wxMessageBox("Failed to load image", "Error", wxOK | wxICON_ERROR);
    return;
  }
  wxStaticBitmap *imageCtrl =
      new wxStaticBitmap(dialog, wxID_ANY, wxBitmap(image));
  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
  sizer->Add(imageCtrl, 1, wxEXPAND | wxALL, 10);
  dialog->SetSizer(sizer);
  dialog->ShowModal();
  dialog->Destroy();
}

void MainFrame::OnFiveStateTimeSequenceMenuItemClicked(wxCommandEvent &event) {
  wxDialog *dialog = new wxDialog(this, wxID_ANY, "Five State Time Sequence",
                                  wxDefaultPosition, wxSize(1800, 600));
  system("dot -Tpng scripts/5.dot -o 5ts.png");
  wxImage image;
  if (!image.LoadFile("5ts.png", wxBITMAP_TYPE_PNG)) {
    wxMessageBox("Failed to load image", "Error", wxOK | wxICON_ERROR);
    return;
  }
  wxStaticBitmap *imageCtrl =
      new wxStaticBitmap(dialog, wxID_ANY, wxBitmap(image));
  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
  sizer->Add(imageCtrl, 1, wxEXPAND | wxALL, 10);
  dialog->SetSizer(sizer);
  dialog->ShowModal();
  dialog->Destroy();
}

void MainFrame::OnSevenStateTimeSequenceMenuItemClicked(wxCommandEvent &event) {
  wxDialog *dialog = new wxDialog(this, wxID_ANY, "Seven State Time Sequence",
                                  wxDefaultPosition, wxSize(1800, 600));
  system("dot -Tpng scripts/7.dot -o 7ts.png");
  wxImage image;
  if (!image.LoadFile("7ts.png", wxBITMAP_TYPE_PNG)) {
    wxMessageBox("Failed to load image", "Error", wxOK | wxICON_ERROR);
    return;
  }
  wxStaticBitmap *imageCtrl =
      new wxStaticBitmap(dialog, wxID_ANY, wxBitmap(image));
  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
  sizer->Add(imageCtrl, 1, wxEXPAND | wxALL, 10);
  dialog->SetSizer(sizer);
  dialog->ShowModal();
  dialog->Destroy();
}

} // namespace view